package org.ielse.frame.view;

import org.ielse.frame.util.BaseUtil;
import org.ielse.frame.util.LogUtil;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.animation.DecelerateInterpolator;

public class PictureView extends ImageView {

	private int imageWidth, imageHeight;
	private int screenWidth, screenHeight;
	private int scaleRatioReferWidth;

	private static final float MIN_MIN = 0.5f;
	private static final float MIN_NORMAL = 1f;
	private static final float MAX_MAX = 7f;
	private static final float MAX_NORMAL = 6f;

	private float downX, downY;
	private double downDist;
	private float oldScaleX, oldScaleY;
	private float oldTranslationX, oldTranslationY;
	private float fingersCenterX, fingersCenterY;

	private boolean scaleMode;

	private boolean anim;

	public PictureView(Context context) {
		this(context, null);
	}

	public PictureView(Context context, AttributeSet attrs) {
		super(context, attrs);
		screenWidth = BaseUtil.getScreenWidth();
		screenHeight = BaseUtil.getScreenHeight();
	}

	@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
		LogUtil.e("PictureView onSizeChanged width:" + w + ",height:" + h);
		scaleRatioReferWidth = w / 3;
	}

	@Override public void setImageBitmap(Bitmap bm) {
		super.setImageBitmap(bm);
		LogUtil.e("PictureView setImageBitmap width:" + bm.getWidth() + ",height:" + bm.getHeight());
		imageWidth = bm.getWidth();
		imageHeight = bm.getHeight();
	}
	
	public void dealTouchEvent(MotionEvent event) {
		if (!anim) {
			switch (event.getAction()) {
			case MotionEvent.ACTION_DOWN:
				downX = event.getRawX();
				downY = event.getRawY();
				oldTranslationX = getTranslationX();
				oldTranslationY = getTranslationY();
				oldScaleX = getScaleX();
				oldScaleY = getScaleY();
				Log.e("Picture", "downX:" + event.getRawX() + ",downY:" + event.getRawY());
			case MotionEvent.ACTION_MOVE:
				if (event.getPointerCount() == 2) {
					Log.e("Picture", "X0:" + event.getX(0) + ",Y0:" + event.getY(0) + "X1:" + event.getX(1) + ",Y1:" + event.getY(1));
					scaleMode = true;
					if (downDist == 0) {
						downDist = calcDistanceBetweenFingers(event);
						
						downX = event.getRawX();
						downY = event.getRawY();
						oldTranslationX = getTranslationX();
						oldTranslationY = getTranslationY();
						oldScaleX = getScaleX();
						oldScaleY = getScaleY();
					}
					double moveDist = calcDistanceBetweenFingers(event);
					if (moveDist != downDist) {
						// LogUtil.e("downDist:" + downDist + ",moveDist:" +
						// moveDist + ",oldScaleX:" + oldScaleX);
						float changedScaleSize = (float) ((moveDist - downDist) / scaleRatioReferWidth);
						// LogUtil.e("changedScaleSize:" + changedScaleSize);
						scalePicture(changedScaleSize);
					}

					if (fingersCenterX == 0 && fingersCenterX == 0) {
						float[] downResult = calcCenterPointBetweenFingers(event);
						fingersCenterX = downResult[0];
						fingersCenterY = downResult[1];
					}

					float[] moveResult = calcCenterPointBetweenFingers(event);
					if (fingersCenterX != moveResult[0] && fingersCenterY != moveResult[1]) {
						final float moveX = moveResult[0] - fingersCenterX;
						final float moveY = moveResult[1] - fingersCenterY;
//						LogUtil.e("fingersCenterY:" + fingersCenterY +", moveResult[1] " + moveResult[1] );
						translatePicture(moveX, moveY);
					}
				}
				else if (event.getPointerCount() == 1 && !scaleMode) {
					Log.e("Picture", "mvoeX:" + event.getRawX() + ",mvoeX:" + event.getRawY());
//					LogUtil.d("single move: x" + event.getX() +",rx" +event.getRawX());
					final float moveX = event.getRawX() - downX;
					final float moveY = event.getRawY() - downY;
					translatePicture(moveX, moveY);
				}
				break;
			case MotionEvent.ACTION_POINTER_UP:
				LogUtil.i("ACTION_POINTER_UP");
				break;
			case MotionEvent.ACTION_CANCEL:
			case MotionEvent.ACTION_UP:
				LogUtil.d("PictureView onTouchEvent ACTION_UP count:" + event.getPointerCount());
				recoverPicture();
				break;
			}
		}
	}

	private void translatePicture(float x, float y) {
		setTranslationX(oldTranslationX + x  /2);
		setTranslationY(oldTranslationY + y  /2);
	}

	private void scalePicture(float changedScaleSize) {
//		LogUtil.e("scalePicture() result:" + (oldScaleX + changedScaleSize) + " ,changed:" + changedScaleSize + ",old:" + oldScaleX);
		if (oldScaleX + changedScaleSize > MIN_MIN && oldScaleX + changedScaleSize < MAX_MAX) {
			setScaleX(oldScaleX + changedScaleSize);
		}
		if (oldScaleY + changedScaleSize > MIN_MIN && oldScaleY + changedScaleSize < MAX_MAX) {
			setScaleY(oldScaleY + changedScaleSize);
		}
	}

	private void recoverPicture() {
		downX = 0;
		downY = 0;
		downDist = 0;
		scaleMode = false;
		fingersCenterX = 0;
		fingersCenterY = 0;

		final float scaleX = getScaleX();
		final float scaleY = getScaleY();
		final float translationX = getTranslationX();
		final float translationY = getTranslationY();

		final float transOffsetX = (imageWidth * scaleX - screenWidth) / 2;
		// LogUtil.e("transOffset:" + transOffsetX + ",translationX:" +
		// translationX);
		ObjectAnimator anim1 = null;
		if (transOffsetX < 0) {
			anim1 = ObjectAnimator.ofFloat(this, "translationX", translationX, 0);
		}
		else if (translationX > transOffsetX) {
			anim1 = ObjectAnimator.ofFloat(this, "translationX", translationX, transOffsetX);
		}
		else if (translationX < -transOffsetX) {
			anim1 = ObjectAnimator.ofFloat(this, "translationX", translationX, -transOffsetX);
		}

		final float transOffsetY = (imageHeight * scaleY - screenHeight) / 2;
		// LogUtil.e("transOffset:" + transOffsetY + ",translationY:" +
		// translationY);
		ObjectAnimator anim2 = null;
		if (transOffsetY < 0) {
			anim2 = ObjectAnimator.ofFloat(this, "translationY", translationY, 0);
		}
		else if (translationY > transOffsetY) {
			anim2 = ObjectAnimator.ofFloat(this, "translationY", translationY, transOffsetY);
		}
		else if (translationY < -transOffsetY) {
			anim2 = ObjectAnimator.ofFloat(this, "translationY", translationY, -transOffsetY);
		}
		ObjectAnimator anim3 = null;
		if (scaleX < MIN_NORMAL) {
			anim3 = ObjectAnimator.ofFloat(this, "scaleX", scaleX, MIN_NORMAL);
		}
		else if (scaleX > MAX_NORMAL) {
			anim3 = ObjectAnimator.ofFloat(this, "scaleX", scaleX, MAX_NORMAL);
		}
		ObjectAnimator anim4 = null;
		if (scaleY < MIN_NORMAL) {
			anim4 = ObjectAnimator.ofFloat(this, "scaleY", scaleY, MIN_NORMAL);
		}
		else if (scaleY > MAX_NORMAL) {
			anim4 = ObjectAnimator.ofFloat(this, "scaleY", scaleY, MAX_NORMAL);
		}

		AnimatorSet set = new AnimatorSet();
		AnimatorSet.Builder b = set.play(ObjectAnimator.ofInt(0));
		if (anim1 != null) b.with(anim1);
		if (anim2 != null) b.with(anim2);
		if (anim3 != null) b.with(anim3);
		if (anim4 != null) b.with(anim4);

		set.setInterpolator(new DecelerateInterpolator());
		set.setDuration(400);
		set.start();
		set.addListener(new AnimatorListenerAdapter() {
			@Override public void onAnimationStart(Animator animation) {
				super.onAnimationStart(animation);
				anim = true;
			}

			@Override public void onAnimationEnd(Animator animation) {
				super.onAnimationEnd(animation);
				anim = false;
			}
		});
	}

	private double calcDistanceBetweenFingers(MotionEvent event) {
		float disX = Math.abs(event.getX(0) - event.getX(1));
		float disY = Math.abs(event.getY(0) - event.getY(1));
		// LogUtil.e("disX:" + disX + ",disY:" + disY);
		return Math.sqrt(disX * disX + disY * disY);
	}

	private float[] calcCenterPointBetweenFingers(MotionEvent event) {
		float xPoint0 = event.getX(0);
		float yPoint0 = event.getY(0);
		float xPoint1 = event.getX(1);
		float yPoint1 = event.getY(1);
//		LogUtil.d("yPoint0:" +yPoint0 +",yPoint1:" +yPoint1);
		return new float[] { (xPoint0 + xPoint1) / 2, (yPoint0 + yPoint1) / 2 };
	}
}
